package task

import (
	"cm2/exception"
	"cm2/exception/taskexception"
	"cm2/service/task/jobs"
	"cm2/service/task/tasks"
	"cm2/service/task/tcp"
	"encoding/json"
	"fmt"
	"net"
	"time"

	"github.com/kataras/golog"
)

// Client 任务TCP连接端
type Client struct {
	SId                     string
	SName                   string
	SType                   string
	Client                  *tcp.Client
	Address                 string
	ServerAddress           string
	createJobLogFn          func(serial string, sid string, code string, name string, tasks []*tasks.Task, state int)
	updateJobTaskLogStateFn func(mode string, serial string, state int, message string, updatedOn time.Time)
}

// NewClient create a new Task TCP Client
func NewClient(sid string, sname string, stype string, serverAddr string) *Client {
	ttc := &Client{
		SId:           sid,
		SName:         sname,
		SType:         stype,
		ServerAddress: serverAddr,
		Client:        tcp.NewClient(sname, serverAddr),
	}

	reportIdentityFn := func(cn *net.TCPConn, logger *golog.Logger) {
		if err := ttc.reportIdentity(); err != nil {
			msg := fmt.Sprintf("could not report identity to server: %v", err)
			logger.Errorf(msg)
			panic(msg)
		}
	}
	ttc.Client.SetOnConnectedFn(reportIdentityFn)

	return ttc
}

// SetLogFn 设置 Log 调用方法
func (ttc *Client) SetLogFn(
	createJobLogFn func(serial string, sid string, code string, name string, tasks []*tasks.Task, state int),
	updateJobTaskLogStateFn func(mode string, serial string, state int, message string, updateOn time.Time),
) {
	ttc.createJobLogFn = createJobLogFn
	ttc.updateJobTaskLogStateFn = updateJobTaskLogStateFn
}

// SetLogger 设置日志
func (ttc *Client) SetLogger(logger *golog.Logger) {
	ttc.Client.SetLogger(logger)
}

// Logger 返回日志记录器
func (ttc *Client) Logger() *golog.Logger {
	return ttc.Client.Logger()
}

// SetReadRetryParams 设置连接读取的重试参数
func (ttc *Client) SetReadRetryParams(times, duration int) {
	ttc.Client.SetReadRetryParams(times, duration)
}

// SetDialRetryParams 设置连接拨号的重试参数
func (ttc *Client) SetDialRetryParams(duration int) {
	ttc.Client.SetDialRetryParams(duration)
}

// SetOnDisconnectFn 设置当有连接中断时的处理，第二个参数为 true 时，为服务端主动关闭连接 TODO: 没用就去掉
func (ttc *Client) SetOnDisconnectFn(fn func(string, bool, *golog.Logger)) {
	ttc.Client.SetOnDisconnectFn(fn)
}

// SetOnJobReceived 设置当客户端接收到任务时进行自定义任务处理
func (ttc *Client) SetOnJobReceived(jpFn func(*jobs.JobConv, func(rpt *Report) *exception.Exception, error)) {
	pkgFn := func(pkg *tcp.Package, conn *net.TCPConn, logger *golog.Logger) {
		// 从pkg中拿出 job
		var job *jobs.JobConv
		if err := json.Unmarshal(pkg.Data, &job); err != nil {
			logger.Errorf("decoding job data failed, %v", err)
			jpFn(nil, ttc.SendReport, err)
		} else {
			logger.Infof("new job received. ser: %s %s", job.Code, job.Serial)
			if jpFn == nil {
				logger.Warn("job handler is not specified")
			} else {
				jpFn(job, ttc.SendReport, nil)
			}
		}
	}
	ttc.Client.SetOnPkgFn(pkgFn)
}

// SendReport 开始发送回报消息
func (ttc *Client) SendReport(rpt *Report) *exception.Exception {
	// 通过连接发送
	err := tcp.SendPackage(
		ttc.SId,
		ttc.Client.Conn,
		rpt,
		ttc.handleReportError,
		ttc.handleReportOk,
		ttc.Logger(),
	)
	if err != nil {
		return exception.NewExceptionWithDefine(taskexception.ReportFailed, err)
	}

	return nil
}

// Start 启动服务
func (ttc *Client) Start() error {
	return ttc.Client.Start()
}

// Shutdown 关闭服务
func (ttc *Client) Shutdown() {
	ttc.Client.Shutdown()
}

func (ttc *Client) reportIdentity() *exception.Exception {
	ttc.Logger().Info("reporting identity to server")
	id := &Identity{
		SId:   ttc.SId,
		SName: ttc.SName,
		SType: ttc.SType,
	}

	report := &Report{
		Mode: IdentityReport,
		Data: id,
	}

	return ttc.SendReport(report)
}

func (ttc *Client) handleReportError(data interface{}, err error) {
	rpt := data.(*Report)

	if rpt.Data == IdentityReport {
		msg := fmt.Sprintf("could not report identity to server: %v", err)
		ttc.Logger().Errorf(msg)
		panic(msg)
	}

	// 处理Execute
}

func (ttc *Client) handleReportOk(data interface{}) {
	rpt := data.(*Report)

	if rpt.Data == IdentityReport {
		return
	}

	// 处理Execute
}
